﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using JasLib.Extensions;
using Microsoft.Win32.SafeHandles;

/// NOTE: Without a parameterless constructor, P/Invoke will throw MissingMethodException for out parameters.
namespace JasLib.Interop.Win32
{
    public class HHOOK : SafeJasLibHandleZeroIsInvalid<HHOOK>
    {
        protected override bool ReleaseHandle()
        {
            /// If the unhook call fails but the hooked thread is dead anyway, then no sweat.
            return (USER32.UnhookWindowsHookEx(handle)) || (KERNEL32.GetLastError() == Win32ErrorCode.ERROR_INVALID_THREAD_ID);
        }
    }

    public class HKEY : SafeJasLibHandleZeroOrMinusOneIsInvalid<HKEY>
    {
        public HKEY()
        {
        }

        private HKEY(uint uiHandle = 0)
            : base(false)
        {
            base.SetHandle((IntPtr)(int)uiHandle);
            return;
        }

        public static readonly HKEY HKEY_CLASSES_ROOT = new HKEY(0x80000000);
        public static readonly HKEY HKEY_CURRENT_USER = new HKEY(0x80000001);
        public static readonly HKEY HKEY_LOCAL_MACHINE = new HKEY(0x80000002);
        public static readonly HKEY HKEY_USERS = new HKEY(0x80000003);
        public static readonly HKEY HKEY_PERFORMANCE_DATA = new HKEY(0x80000004);
        public static readonly HKEY HKEY_CURRENT_CONFIG = new HKEY(0x80000005);
        public static readonly HKEY HKEY_DYN_DATA = new HKEY(0x80000006);
        public static readonly HKEY HKEY_CURRENT_USER_LOCAL_SETTINGS = new HKEY(0x80000007);
        public static readonly HKEY HKEY_PERFORMANCE_TEXT = new HKEY(0x80000050);
        public static readonly HKEY HKEY_PERFORMANCE_NLSTEXT = new HKEY(0x80000060);

        protected override bool ReleaseHandle()
        {
            return (ADVAPI32.RegCloseKey(handle) == Win32ErrorCode.ERROR_SUCCESS);
        }
    }

    /// <summary>
    /// Handle to a keyboard layout.
    /// </summary>
    public class HKL : SafeJasLibHandleZeroIsInvalid<HKL>
    {
        protected override bool ReleaseHandle()
        {
            return USER32.UnloadKeyboardLayout(handle);
        }
    }

    public class LPITEMIDLIST : SafeCoTaskAllocatedPointer<LPITEMIDLIST>
    {
        public LPITEMIDLIST()
            : this(default(IntPtr))
        {
        }

        public LPITEMIDLIST(IntPtr newHandle)
            : base(true)
        {
            handle = newHandle;
        }

        public static implicit operator LPITEMIDLIST(UINT_PTR uiSource)
        {
            return new LPITEMIDLIST(uiSource);
        }
    }

    public class LPSAFEARRAY : SafeJasLibHandleZeroIsInvalid<LPSAFEARRAY>
    {
        protected override bool ReleaseHandle()
        {
            return OLEAUT32.SafeArrayDestroy(handle).SUCCEEDED();
        }
    }

    public class PTP_CLEANUP_GROUP : SafeJasLibHandleZeroIsInvalid<PTP_CLEANUP_GROUP>
    {
        protected override bool ReleaseHandle()
        {
            KERNEL32.CloseThreadpoolCleanupGroup(handle);
            return true;
        }
    }

    public class PTP_IO : SafeJasLibHandleZeroIsInvalid<PTP_IO>
    {
        protected override bool ReleaseHandle()
        {
            KERNEL32.CloseThreadpoolIo(handle);
            return true;
        }
    }

    public class PTP_POOL : SafeJasLibHandleZeroIsInvalid<PTP_POOL>
    {
        protected override bool ReleaseHandle()
        {
            KERNEL32.CloseThreadpool(handle);
            return true;
        }
    }

    public class PTP_TIMER : SafeJasLibHandleZeroIsInvalid<PTP_TIMER>
    {
        protected override bool ReleaseHandle()
        {
            KERNEL32.CloseThreadpoolTimer(handle);
            return true;
        }
    }

    public class PTP_WAIT : SafeJasLibHandleZeroIsInvalid<PTP_WAIT>
    {
        protected override bool ReleaseHandle()
        {
            KERNEL32.CloseThreadpoolWait(handle);
            return true;
        }
    }

    public class PTP_WORK : SafeJasLibHandleZeroIsInvalid<PTP_WORK>
    {
        protected override bool ReleaseHandle()
        {
            KERNEL32.CloseThreadpoolWork(handle);
            return true;
        }
    }

    public class SafeCoTaskAllocatedPointer<T> : SafeJasLibHandleZeroIsInvalid<T>
        where T : SafeCoTaskAllocatedPointer<T>, new()
    {
        public SafeCoTaskAllocatedPointer(bool bOwnsHandle)
            : base(bOwnsHandle)
        {
        }

        protected override bool ReleaseHandle()
        {
            OLE32.CoTaskMemFree(handle);
            return true;
        }
    }

    /// <summary>
    /// This is necessary because SafeWaitHandle is abstract.
    /// </summary>
    public class SafeKernelWaitHandle<T> : SafeJasLibHandleZeroOrMinusOneIsInvalid<T>
        where T : SafeKernelWaitHandle<T>, new()
    {
        /// <summary>
        /// See the remarks.
        /// </summary>
        public static readonly T INVALID_HANDLE_VALUE = new T();

        static SafeKernelWaitHandle()
        {
            INVALID_HANDLE_VALUE.SetHandle(new IntPtr(-1));
            return;
        }

        public SafeKernelWaitHandle()
        {
        }

        public SafeKernelWaitHandle(bool bOwnsHandle)
            : base(bOwnsHandle)
        {
        }

        public static implicit operator HANDLE(SafeKernelWaitHandle<T> handle)
        {
            return handle.DangerousGetHandle();
        }

        public static implicit operator IntPtr(SafeKernelWaitHandle<T> handle)
        {
            return handle.DangerousGetHandle();
        }
    }

    public class SafeKernelEventHandle : SafeKernelWaitHandle<SafeKernelEventHandle>
    {
        public SafeKernelEventHandle()
            : this(true)
        {
        }

        public SafeKernelEventHandle(bool bOwnsHandle)
            : base(bOwnsHandle)
        {
        }

        public static implicit operator SafeKernelEventHandle(EventWaitHandle waitEvent)
        {
            var handle = new SafeKernelEventHandle(false);
            handle.SetHandle(waitEvent.Handle);
            return handle;
        }
    }

    public class SafeProcessHandle : SafeKernelWaitHandle<SafeProcessHandle>
    {
        public static implicit operator SafeProcessHandle(Process process)
        {
            /// TODO: Use reflection to get the handle. :(
            throw new NotImplementedException();
        }
    }

    public class SafeThreadHandle : SafeKernelWaitHandle<SafeThreadHandle>
    {
        public static implicit operator SafeThreadHandle(Thread thread)
        {
            /// TODO: Use reflection to get the handle. :(
            throw new NotImplementedException();
        }
    }

    /// <summary>
    /// Service control handle.
    /// </summary>
    public class SC_HANDLE : SafeJasLibHandleZeroIsInvalid<SC_HANDLE>
    {
        protected override bool ReleaseHandle()
        {
            return ADVAPI32.CloseServiceHandle(handle);
        }
    }
}
